﻿using System;
using System.Collections.Generic;
using System.Text;
using WcfServiceBomberman.Contracts.Duplex;

namespace WcfServiceBomberman.MatchMaking
{
    public class Queue : Context.Handler
    {
        uint player_per_team;
        uint nb_team;
        LinkedList<Player> players;
        Player[] team;
        Contracts.Duplex.Type type;

        protected Queue(Contracts.Duplex.Type type, uint player_per_team,uint nb_team)
        {
            type = this.type;
            players = new LinkedList<Player>();
            team = new Player[nb_team * player_per_team];
            this.player_per_team = player_per_team;
            this.nb_team = nb_team;
        }

        // compute full list one time
        public void fetch()
        {
            if (players.Count < 2)
                return;

            DateTime now = DateTime.Now;
            //now.AddDays(1.0);

            for (LinkedListNode<Player> node = players.First; node != null; node = node.Next )
            {
                Player player = node.Value;

                // we sort the player list and remove the focused player
                SortedSet<RangedPlayer> sorted = sort(player);

                // we make a new linked from the sorted list
                LinkedList<Player> potential_players = new LinkedList<Player>();
                foreach (RangedPlayer ranged in sorted)
                    potential_players.AddLast(ranged.player);

                // we filter the list then fetch players
                if (fetch_recurrent(filter(potential_players, player, now), 1, now))
                {
                    team[0] = player;
                    // we found a team
                    createMatch();
                    // remove players from queue
                    foreach(Player lucker in team)
                        players.Remove(lucker);
                }
            }
        }

        // returns the new filtered list based on the focused player
        private LinkedList<Player> filter(LinkedList<Player> available, Player focus, DateTime time)
        {
            LinkedList<Player> filtred = new LinkedList<Player>();
            foreach(Player player in available) {
                if (focus == player) continue;
                if(willFight(focus,player,time))
                    filtred.AddLast(player);
                }
            return filtred;
        }

        private bool willFight(Player a, Player b, DateTime time)
        {
            int elapsed_a = a.elapsed(time);
            int elapsed_b = b.elapsed(time);
            if (a.willForce(elapsed_a) || b.willForce(elapsed_b))
                return true;
            return ((a.willAccept(b, elapsed_a)) && (b.willAccept(a, elapsed_b)));              
        }

        private bool fetch_recurrent(LinkedList<Player> available, int index,DateTime time)
        {
            // reached the end
            if (index == team.Length)
                return true;

            foreach (Player player in available)
            {
                // we filter the list the fetch players
                if (fetch_recurrent(filter(available, player, time), index + 1, time))
                {
                    team[index] = player;
                    return true;
                }

            }
            return false;
        }

        protected void createMatch()
        {
            List<Team> teams = new List<Team>();
            for(int index_team = 0; index_team < nb_team; index_team++) {
                Team team = new Team();
                teams.Add(team);
                team.id = (ushort) index_team;
                for (int index_player = 0; index_player < player_per_team; index_player++)
                {
                    team.players.Add(this.team[player_per_team * index_team + index_player].player);
                }
            }
            
            // the callback
            context.server.createGame(new Match((UInt16)context.randomizer.Next(UInt16.MaxValue),type, teams));
        }

        public void add(Player item)
        {
            if(players.Count == 0) {
                players.AddFirst(item);
                return;
            }
            for(LinkedListNode<Player> player = players.First; player != null; player = player.Next) {
                if (item.CompareTo(player.Value) < 1)
                {
                    players.AddBefore(player, item);
                }
            }
        }

        private SortedSet<RangedPlayer> sort(Player target)
        {
            // make a new sorted list
            SortedSet<RangedPlayer> sorted = new SortedSet<RangedPlayer>();
            foreach (Player player in players)
            {
                if (player == target) continue;
                sorted.Add(new RangedPlayer(player, target));
            }
            return sorted;
        }
    }
}
